/**
 * Title: DbUtil
 * Description: Copyright: Copyright (c) 2019 Company: Sharp
 *
 * @author Sharp
 * @date 2019-4-4 15:48
 * @description Project Name: Tanya
 * Package: com.srct.service.utils
 */
package com.sharp.kernel.utils;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import com.sharp.kernel.constant.db.SqlConstant;
import com.sharp.kernel.vo.query.QueryReqVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class DbUtil {
    public static PageInfo<?> buildPageInfo(QueryReqVO req) {
        PageInfo<?> pageInfo = new PageInfo<>();
        if (req.getCurrentPage() != null) {
            pageInfo.setPageNum(req.getCurrentPage());
        }
        if (req.getPageSize() != null) {
            pageInfo.setPageSize(req.getPageSize());
        }
        return pageInfo;
    }

    public static <T> PageInfo<T> buildPageInfo(QueryReqVO req, Class<T> expectedClass) {
        PageInfo<T> pageInfo = new PageInfo<>();
        if (req.getCurrentPage() != null) {
            pageInfo.setPageNum(req.getCurrentPage());
        }
        if (req.getPageSize() != null) {
            pageInfo.setPageSize(req.getPageSize());
        }
        return pageInfo;
    }

    public static PageInfo<?> buildPageInfo(Integer currentPage, Integer pageSize) {
        PageInfo<?> pageInfo = new PageInfo<>();
        if (currentPage != null) {
            pageInfo.setPageNum(currentPage);
        }
        if (pageSize != null) {
            pageInfo.setPageSize(pageSize);
        }
        return pageInfo;
    }

    public static <T> PageInfo<T> buildPageInfo(Integer currentPage, Integer pageSize, Class<T> expectedClass) {
        PageInfo<T> pageInfo = new PageInfo<>();
        if (currentPage != null) {
            pageInfo.setPageNum(currentPage);
        }
        if (pageSize != null) {
            pageInfo.setPageSize(pageSize);
        }
        return pageInfo;
    }

    @SuppressWarnings("rawtypes")
    public static <K, T> PageInfo<T> copy(PageInfo<K> source, Class<T> clazz) {
        PageInfo<T> res = new PageInfo(source.getList());
        if (CollectionUtils.isNotEmpty(source.getList())) {
            List<T> tempList = ReflectionUtil.copyList(source.getList(), clazz);
            res.setList(tempList);
        }
        res.setTotal(source.getTotal());
        return res;
    }

    public static <K, T> PageInfo<T> fromPageInfo(PageInfo<K> source) {
        PageInfo<T> res = new PageInfo(source.getList());
        res.setTotal(source.getTotal());
        res.setPages(source.getPages());
        res.setPageNum(source.getPageNum());
        res.setPageSize(source.getPageSize());
        return res;
    }

    public static <T> Page<T> copy(List<T> list) {
        Page<T> res;
        if (list instanceof Page) {
            Page<T> page = (Page<T>) list;
            res = new Page<T>(page.getPageNum(), page.getPageSize());
            res.setTotal(page.getTotal());
        } else {
            res = new Page<T>();
        }
        return res;
    }

    public static <T> List<T> resultMapConvert(List<?> dataList, Class<T> targetClass) {
        return resultMapConvert(dataList, targetClass, false);
    }

    /**
     * 将多表联查结果映射成 一对多/一对一 对象形如
     * class extends oneClass {
     *
     * @param dataList        多表联查原始数据
     * @param targetClass     用转换的对象类型
     * @param <T>             对象类型
     * @param filterDuplicate 是否过滤重复对象
     * @return 拼接后的对象
     * @MyResultMap private toOneClass one
     * private List<toManyClass> many;
     * }
     * 一对一 查找 标注@MyResultMap注解的对象
     * 一对多 查找List类型属性进行映射
     * 其他属性直接按照名称和类型赋值
     * 即只支持List类型进行一对多属性映射
     */

    public static <T> List<T> resultMapConvert(List<?> dataList, Class<T> targetClass, Boolean filterDuplicate) {
        List<Field> resultMapFieldList = ReflectionUtil.getFields(targetClass).stream()
                .filter(field -> field.getType().equals(List.class) || field.getAnnotation(MyResultMap.class) != null)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(resultMapFieldList)) {
            if (filterDuplicate) {
                return new ArrayList(ReflectionUtil.copySet(dataList, targetClass));
            } else {
                return ReflectionUtil.copyList(dataList, targetClass);
            }
        }
        List<T> res = new ArrayList<>();

        Map<T, List<Object>> map = new LinkedHashMap<>();
        for (Object data : dataList) {
            T parentObj = ReflectionUtil.copy(data, targetClass);
            List<Object> many = map.getOrDefault(parentObj, new ArrayList<>());
            many.add(data);
            map.put(parentObj, many);
        }

        for (Map.Entry<T, List<Object>> entry : map.entrySet()) {
            for (Field resultMapField : resultMapFieldList) {
                Class<?> resultMapClass = resultMapField.getType();
                if (resultMapField.getAnnotation(MyResultMap.class) != null) {
                    filterDuplicate =
                            filterDuplicate || resultMapField.getAnnotation(MyResultMap.class).filterDuplicate();
                }
                boolean isCollectResultMap = (List.class).equals(resultMapClass);
                if (isCollectResultMap) {
                    ParameterizedType genericType = (ParameterizedType) resultMapField.getGenericType();
                    resultMapClass = (Class<?>) genericType.getActualTypeArguments()[0];
                }

                List<?> list = resultMapConvert(entry.getValue(), resultMapClass, filterDuplicate);
                if (isCollectResultMap) {
                    ReflectionUtil.setValue(resultMapField, entry.getKey(), list);
                } else {
                    ReflectionUtil.setValue(resultMapField, entry.getKey(), list.get(0));
                }
            }
            res.add(entry.getKey());
        }
        return res;
    }

    public static void setCreateAtTimeStamp(Object entity) {
        ReflectionUtil.setFieldValue(entity, SqlConstant.CREATE_FIELD, new Date());
    }

    public static void setUpdateAtTimeStamp(Object entity) {
        ReflectionUtil.setFieldValue(entity, SqlConstant.UPDATE_FIELD, new Date());
    }

    public static <T> T buildEntity(Map<String, Object> entityMap, Class<T> clazz) {
        return ReflectionUtil.copy(entityMap, clazz);
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD})
    public @interface MyResultMap {
        String value() default "";

        boolean filterDuplicate() default false;
    }
}
